package com.mmd.tencent.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.mmd.base.utils.EncryptionUtils;
import com.mmd.core.utils.RandomUtils;
import com.mmd.tencent.config.WechatPublicConfig;
import com.mmd.tencent.config.WechatUrlConfig;
import com.mmd.tencent.entity.*;
import com.mmd.tencent.service.WechatPublicService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.Map;
import java.util.StringJoiner;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@ConditionalOnProperty(name = "appid",prefix = "wechat.public")
public class WechatPublicServiceImpl implements WechatPublicService {
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private WechatPublicConfig wechatPublicConfig;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private RedissonClient redissonClient;

    /**
     * 获取微信用户信息 和 refreshToken
     *
     * @param code String
     */
    @Override
    public WechatUserEntity getUserInfo(String code) {
        String url = WechatUrlConfig.loginUrl.replaceAll("\\{code}", code).replaceAll("\\{appid}",wechatPublicConfig.getAppid()).replaceAll("\\{secret}",wechatPublicConfig.getAppSecret());
        ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
        return JSONObject.parseObject(forEntity.getBody()).toJavaObject(WechatUserEntity.class);
    }

    /**
     * 微信小程序获取用户信息
     *
     * @param code String
     */
    @Override
    public WechatMiniUserEntity getUserInfoMini(String code) {
        String url = WechatUrlConfig.loginUrlMini.replaceAll("\\{code}", code).replaceAll("\\{appid}",wechatPublicConfig.getAppid()).replaceAll("\\{secret}",wechatPublicConfig.getAppSecret());
        ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
        return JSONObject.parseObject(forEntity.getBody()).toJavaObject(WechatMiniUserEntity.class);
    }

    /**
     * 刷新accessToken
     */
    @Override
    public WechatUserEntity refresh(String refreshToken) {
        String url = WechatUrlConfig.accessTokenUrl.replaceAll("\\{refreshToken}", refreshToken).replaceAll("\\{appid}",wechatPublicConfig.getAppid()).replaceAll("\\{secret}",wechatPublicConfig.getAppSecret());
        ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
        return JSONObject.parseObject(forEntity.getBody()).toJavaObject(WechatUserEntity.class);
    }

    /**
     * 获取用户信息
     */
    @Override
    public WechatUserInfoEntity userInfo(String accessToken, String openId)
    {
        String url = WechatUrlConfig.userinfoUrl.replaceAll("\\{accessToken}", accessToken).replaceAll("\\{openId}", openId).replaceAll("\\{appid}",wechatPublicConfig.getAppid()).replaceAll("\\{secret}",wechatPublicConfig.getAppSecret());
        ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
        return JSONObject.parseObject(forEntity.getBody()).toJavaObject(WechatUserInfoEntity.class);
    }

    /**
     * 获取accessToken
     */
    @Override
    public WechatAccessToken unifyAccessTokenUrl()
    {
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        String key = "public-wx-access-token";
        String value = valueOperations.get(key);
        if(!StringUtils.isEmpty(value)){
            return JSONObject.parseObject(value).toJavaObject(WechatAccessToken.class);
        }
        RLock lock = redissonClient.getLock("public-wx-access-token-lock");
        try {
            if (!lock.tryLock(6,5L, TimeUnit.SECONDS)) {return null;}
            value = valueOperations.get(key);
            if(!StringUtils.isEmpty(value)) return JSONObject.parseObject(value).toJavaObject(WechatAccessToken.class);
            ResponseEntity<String> forEntity = restTemplate.getForEntity(WechatUrlConfig.unifyAccessTokenUrl.replaceAll("\\{appid}",wechatPublicConfig.getAppid()).replaceAll("\\{secret}",wechatPublicConfig.getAppSecret()), String.class);
            WechatAccessToken wechatAccessToken = JSONObject.parseObject(forEntity.getBody()).toJavaObject(WechatAccessToken.class);
            valueOperations.set(key,JSONObject.toJSONString(wechatAccessToken), Long.parseLong(wechatAccessToken.getExpiresIn()) - 200, TimeUnit.SECONDS);
            return wechatAccessToken;
        } catch (InterruptedException ignored) {
        }finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return null;
    }

    @Override
    public JsapiTicket jsapiTicket() {
        WechatAccessToken wechatAccessToken = unifyAccessTokenUrl();
        return jsapiTicket(wechatAccessToken.getAccessToken());
    }

    /**
     * 获取jsapiTicket
     */
    @Override
    public JsapiTicket jsapiTicket(String accessToken)
    {
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        String key = "public-wx-jsapi-ticket";
        String value = valueOperations.get(key);
        if(!StringUtils.isEmpty(value)) return JSONObject.parseObject(value).toJavaObject(JsapiTicket.class);
        RLock lock = redissonClient.getLock("public-wx-jsapi-ticket-lock");
        try {
            if (!lock.tryLock(6,5, TimeUnit.SECONDS)) {return null;}
            value = valueOperations.get(key);
            if(!StringUtils.isEmpty(value)) return JSONObject.parseObject(value).toJavaObject(JsapiTicket.class);
            String url = WechatUrlConfig.jsapiTicket.replaceAll("\\{accessToken}", accessToken).replaceAll("\\{appid}",wechatPublicConfig.getAppid()).replaceAll("\\{secret}",wechatPublicConfig.getAppSecret());
            ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
            JsapiTicket jsapiTicket = JSONObject.parseObject(forEntity.getBody()).toJavaObject(JsapiTicket.class);
            valueOperations.set(key,JSONObject.toJSONString(jsapiTicket), Long.parseLong(jsapiTicket.getExpiresIn()) - 200, TimeUnit.SECONDS);
            return jsapiTicket;
        } catch (InterruptedException ignored) {
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return null;
    }

    /**
     * 微信分享
     * 不要hash路由 这里一定不要编码 必须是正常的字符 例如http://commontest.natapp1.cc/pages/help/index?projectId=41
     */
    @Override
    public WechatShareEntity wechatShareEntity(String url) throws NoSuchAlgorithmException {
        return wechatShareEntity("url",url);
    }

    /**
     * 获取微信基础数据
     * @param key 加签键名
     * @param value 加签值
     */
    @Override
    public WechatShareEntity wechatShareEntity(String key, String value) throws NoSuchAlgorithmException {
        JsapiTicket jsapiTicket = jsapiTicket();
        TreeMap<String, Object> treeMap = new TreeMap<>();
        treeMap.put("noncestr", RandomUtils.aAndZ0And9(32));
        treeMap.put("jsapi_ticket", jsapiTicket.getTicket());
        treeMap.put("timestamp", (new Date().getTime()) / 1000);
        if(!StringUtils.isEmpty(key)){
            treeMap.put(key, value);
        }
        StringJoiner stringJoiner = new StringJoiner("&");
        for (Map.Entry<String, Object> stringObjectEntry : treeMap.entrySet()) {
            stringJoiner.add(stringObjectEntry.getKey()+"="+stringObjectEntry.getValue());
        }
        String signature = EncryptionUtils.encryptionSHA1(stringJoiner.toString());
        System.err.println(stringJoiner.toString());
        WechatShareEntity wechatShareEntity = new WechatShareEntity();
        wechatShareEntity.setAppid(wechatPublicConfig.getAppid());
        wechatShareEntity.setNonceStr((String) treeMap.get("noncestr"));
        wechatShareEntity.setSignature(signature);
        wechatShareEntity.setTimestamp(Long.valueOf(String.valueOf(treeMap.get("timestamp"))));
        return wechatShareEntity;
    }

}
